package ws2

import (
	"time"
	"net"
	"sync"
	"errors"
	"encoding/base64"
	"fmt"

	"github.com/gorilla/websocket"
)

type webSocketConn struct {
	sync.Mutex
	conn 		*websocket.Conn
	isClosed	bool

	msgChan 	chan []byte

	bufType 	int 	// 所传输的数据类型 0未知 1 base64 2二进制
	fullTimes	int 	// 发送缓冲区满的次数
}

func NewWebSocketConn(conn *websocket.Conn) *webSocketConn {
	c := &webSocketConn{}
	c.conn = conn
	c.isClosed = false
	c.msgChan = make(chan []byte, 1024)
	go c.Run()
	return c
}

func (conn *webSocketConn) Run() {
	defer func() {
		if err := recover(); err != nil {
			fmt.Println(err)
		}
	} ()
	for buf := range conn.msgChan {
		if !conn.isClosed {
			t := websocket.BinaryMessage
			if 1 == conn.bufType {
				t = websocket.TextMessage
			}
			w, err := conn.conn.NextWriter(t)
			if nil != err {
				fmt.Println("webSocketConn NextWriter err", err)
				return
			}
			w.Write(buf)
			if err = w.Close(); nil != err {
				fmt.Println("webSocketConn Writer close err", err)
				return
			}
		} else {
			return
		}
	}
}

func (conn *webSocketConn) send(buf []byte) {
	defer func() {
		if err := recover(); err != nil {
			fmt.Println(err)
		}
	} ()

	if len(conn.msgChan) < 1024 {
		conn.msgChan <- buf	
	} else {
		if ConnBuffFullAction == CONN_BUF_FULL_CLOSE {
			fmt.Println("webSocketConn msgChan too big", conn.RemoteAddr().String(), conn.isClosed)
			conn.doClose()
		} else {
			conn.fullTimes++
			if conn.fullTimes >= 1024 {
				fmt.Println("webSocketConn msgChan too big", conn.RemoteAddr().String(), conn.isClosed)
				conn.doClose()
			}
		}
	}
}

func (conn *webSocketConn) WriteMsg(args ...[]byte)  (int, error) {
	conn.Lock()
	defer conn.Unlock()
	if !conn.isClosed {
		msgLen := 0
		for i := 0; i < len(args); i++ {
			msgLen += len(args[i])
		}
		if msgLen > 0 {
			msg := make([]byte, msgLen)
			lenght := 0
			for i := 0; i < len(args); i++ {
				copy(msg[lenght:], args[i])
				lenght += len(args[i])
			}

			if 1 == conn.bufType {
				buf := make([]byte, base64.StdEncoding.EncodedLen(len(msg)))
				base64.StdEncoding.Encode(buf, msg)
				conn.send(buf)
				return len(buf), nil
			} else {
				conn.send(msg)
				return len(msg), nil
			}
		}
	}
	return 0, errors.New("conn is closed")
}
	
func (conn *webSocketConn) WriteWithBase64(buf []byte, buf64 []byte)  (int, error) {
	conn.Lock()
	defer conn.Unlock()
	if !conn.isClosed {
		if 1 == conn.bufType {
			conn.send(buf64)
			return len(buf64), nil
		} else {
			conn.send(buf)
			return len(buf), nil
		}
	}
	return 0, errors.New("conn is closed")
}

func (conn *webSocketConn) ReadMsg()([]byte, error) {
	messageType, buf, err := conn.conn.ReadMessage()
	if nil == err && len(buf) > 0 {
		if 0 == conn.bufType {
			if messageType == websocket.TextMessage {
				conn.bufType = 1
			} else {
				conn.bufType = 2
			}
		}
		if 1 == conn.bufType {
			rb, err := base64.StdEncoding.DecodeString(string(buf))
			return rb, err
		} else {
			return buf, err
		}
	}
	return nil, err
}

func (conn *webSocketConn) LocalAddr() net.Addr {
	return conn.conn.LocalAddr()
}

func (conn *webSocketConn) RemoteAddr() net.Addr {
	return conn.conn.RemoteAddr()
}

func (conn *webSocketConn) Close() {
	conn.Lock()
	defer conn.Unlock()

	conn.doClose()
}

func (conn *webSocketConn) doClose() {
	if !conn.isClosed {
		// conn.conn.SetReadDeadline(time.Now().Add(10 * time.Second))
		conn.conn.Close()
		conn.isClosed = true

		close(conn.msgChan)
	}
}

func (conn *webSocketConn) SetReadDeadline(t time.Time) {
	conn.Lock()
	defer conn.Unlock()
	if !conn.isClosed {
		conn.conn.SetReadDeadline(t)
	}
}
